//
// Created by Scave on 2024/2/24.
//

#include <cmath>
#include "MeshUtil.h"
#include <iostream>
#include <limits>


NS_RIDER_BEGIN
    const Color MeshUtil::kDefaultRectColor = {1, 1, 1, 1};
    const Color MeshUtil::kDefaultBackColor = Colors::Black;
    const Color MeshUtil::kDefaultBoxColors[6] = {
            Colors::Red, Colors::Green, Colors::Blue,
            Colors::Orange, Colors::Purple, Colors::Cyan
    };
    const Color MeshUtil::kDefaultTriangularPyramidColor[4] = {
            Colors::Blue, Colors::Red,
            Colors::Orange, Colors::Purple
    };
    const double PI = 3.141592653589793;

    void MeshUtil::BuildRectangle(Mesh* mesh, float width, float height, Color color) {
        float w = width / 2.f;
        float h = height / 2.f;
        Vertex v1 = {{w, h, 0}, {}, {1, 1}, color};
        Vertex v2 = {{-w, h, 0}, {}, {0, 1}, color};
        Vertex v3 = {{-w, -h, 0}, {}, {0, 0}, color};
        Vertex v4 = {{w, -h, 0}, {}, {1, 0}, color};
        mesh->AddQuad(v1, v2, v3, v4);
    }

    void MeshUtil::BuildTriangle(Mesh* mesh, float width, float height, Color color) {
        float w = width / 2.f;
        float h = height / 2.f;
        Vertex v1 = {{0, h, 0}, {}, {0.5, 1}, color};
        Vertex v2 = {{-w, -h, 0}, {}, {0, 0}, color};
        Vertex v3 = {{w, -h, 0}, {}, {1, 0}, color};
        mesh->AddFace(v1, v2, v3);
    }

    void MeshUtil::BuildBox(Mesh* mesh, float width, float height, float depth, Color colors[6]) {
        float w = width / 2.f;
        float h = height / 2.f;
        float d = depth / 2.f;

        // front
        Color color = colors[0];
        Vertex front_v1 = {{-w, +h, +d}, Vectors::Forward, {0, 0}, color};
        Vertex front_v2 = {{-w, -h, +d},  Vectors::Forward, {0, 1},color};
        Vertex front_v3 = {{+w, -h, +d}, Vectors::Forward, {1, 1}, color};
        Vertex front_v4 = {{+w, +h, +d}, Vectors::Forward, {1, 0}, color};
        mesh->AddQuad(front_v1, front_v2, front_v3, front_v4);
        // right
        color = colors[1];
        Vertex right_v1 = {{+w, +h, +d}, Vectors::Right, {0, 0}, color};
        Vertex right_v2 = {{+w, -h, +d}, Vectors::Right, {0, 1}, color};
        Vertex right_v3 = {{+w, -h, -d}, Vectors::Right, {1, 1}, color};
        Vertex right_v4 = {{+w, +h, -d}, Vectors::Right, {1, 0}, color};
        mesh->AddQuad(right_v1, right_v2, right_v3, right_v4);
        // back
        color = colors[2];
        Vertex back_v1 = {{+w, +h, -d}, Vectors::Back, {0, 0}, color};
        Vertex back_v2 = {{+w, -h, -d}, Vectors::Back, {0, 1}, color};
        Vertex back_v3 = {{-w, -h, -d}, Vectors::Back, {1, 1}, color};
        Vertex back_v4 = {{-w, +h, -d}, Vectors::Back, {1, 0}, color};
        mesh->AddQuad(back_v1, back_v2, back_v3, back_v4);
        // left
        color = colors[3];
        Vertex left_v1 = {{-w, +h, -d}, Vectors::Left, {0, 0}, color};
        Vertex left_v2 = {{-w, -h, -d}, Vectors::Left, {0, 1}, color};
        Vertex left_v3 = {{-w, -h, +d}, Vectors::Left, {1, 1}, color};
        Vertex left_v4 = {{-w, +h, +d}, Vectors::Left, {1, 0}, color};
        mesh->AddQuad(left_v1, left_v2, left_v3, left_v4);
        // top
        color = colors[4];
        Vertex top_v1 = {{-w, +h, -d}, Vectors::Up, {0, 0}, color};
        Vertex top_v2 = {{-w, +h, +d}, Vectors::Up, {0, 1}, color};
        Vertex top_v3 = {{+w, +h, +d}, Vectors::Up, {1, 1}, color};
        Vertex top_v4 = {{+w, +h, -d}, Vectors::Up, {1, 0}, color};
        mesh->AddQuad(top_v1, top_v2, top_v3, top_v4);
        // bottom
        color = colors[5];
        Vertex bottom_v1 = {{+w, -h, -d}, Vectors::Down, {0, 0}, color};
        Vertex bottom_v2 = {{+w, -h, +d}, Vectors::Down, {0, 1}, color};
        Vertex bottom_v3 = {{-w, -h, +d}, Vectors::Down, {1, 1}, color};
        Vertex bottom_v4 = {{-w, -h, -d}, Vectors::Down, {1, 0}, color};
        mesh->AddQuad(bottom_v1, bottom_v2, bottom_v3, bottom_v4);
    }

    /*
    * 构建球体
    * @param mesh 顶点容器
    * @param sectors 经度数，横向分块
    * @param stacks 纬度数，纵向分块
    * @param radius 半径
    * @param y_move 垂直偏移距离
    * @param type 球体类型
    * @param color 颜色
    * phi为线与y轴的夹角
    * theta为投影与x轴的夹角
    * x = r * sin(phi) * cos(theta)
    * y = r * cos(phi)
    * z = r * sin(phi) * sin(theta)
    */
    void MeshUtil::BuildSphereImpl(Mesh* mesh, uint16_t sectors, uint16_t stacks, float radius, float y_move, SphereType type, Color color) {
        // 预分配，减少扩容开销
        auto& datas = mesh->vertex_data_list;
        auto& indices = mesh->vertex_index_list;

        uint16_t data_size = datas.size();
        uint16_t index_size = indices.size();
        uint16_t stacks_loop = (type == SphereType::All) ? stacks : stacks / 2;
        datas.reserve(data_size + (sectors + 1) * (stacks_loop + 1));
        indices.reserve(index_size + 6 * sectors * stacks_loop);

        float sector_step = 2.f * PI / sectors;
        float stack_step = PI / stacks;
        
        for (int i = 0; i <= stacks_loop; i++) {
            // All : [-pi / 2, pi / 2]
            // Top : [0, pi / 2]
            // Bottom : [-pi / 2, 0]
            float phi = (type == SphereType::Bottom) ? (-i * stack_step) : (PI / 2.f - i * stack_step);
            float xz = radius * std::cos(phi);
            float y = radius * std::sin(phi) + y_move;

            for (int j = 0; j <= sectors; j++) {
                float theta = j * sector_step;
                float x = xz * std::cos(theta);
                float z = xz * std::sin(theta);
                Vertex v = {{x, y, z}, {}, {(float)j / sectors, (float)i / stacks_loop}, Colors::Green};
                if (j > sectors / 2) v.color = Colors::Red;

                datas.push_back(v);
            }
        }

        for (int i = 0; i < stacks_loop; i++) {
            int k1 = i * (sectors + 1) + data_size;
            int k2 = k1 + sectors + 1;
            for (int j = 0; j < sectors; j++, k1++, k2++) {
                uint16_t idx1 = k1;
                uint16_t idx2 = k2;
                uint16_t idx3 = k1 + 1;
                uint16_t idx4 = k2 + 1;
                if (!(i == 0 && type == SphereType::Top)) { // 不是第一个堆栈，生成上三角
                    indices.push_back(idx1);
                    indices.push_back(idx2);
                    indices.push_back(idx3);
                }
                if (!(i == stacks_loop - 1 && type == SphereType::Bottom)) { // 不是最后一个堆栈，生成下三角
                    indices.push_back(idx2);
                    indices.push_back(idx4);
                    indices.push_back(idx3);
                }
            }
        }
    }

    /*
    1 : 顺时针
    2 : 逆时针
    3 : 共线
    */
    int MeshUtil::ClockOrder(const Mesh* mesh, uint16_t idx1, uint16_t idx2, uint16_t idx3, Vec3 normal) {
        Vec3 a = mesh->vertex_data_list[idx1].position;
        Vec3 o = mesh->vertex_data_list[idx2].position;
        Vec3 b = mesh->vertex_data_list[idx3].position;

        Vec3 v = Vec3::Sub(a, o);
        Vec3 u = Vec3::Sub(b, o);
        Vec3 cross = Vec3::Cross(u, v);
        float dot = Vec3::Dot(cross, normal);
        
        if (dot > 1e-6) return 1;
        if (dot < -1e-6) return 2;
        return 3;
    }

    void MeshUtil::BuildSphere(Mesh* mesh, uint16_t sectors, uint16_t stacks, float radius, Color color) {
        BuildSphereImpl(mesh, sectors, stacks, radius, 0.f, SphereType::All, color);
    }

    void MeshUtil::BuildSkyBox(Mesh* mesh, float size, float quality) {

    }

    void MeshUtil::BuildCapsule(Mesh* mesh, uint16_t sectors, uint16_t stacks, float radius, float length, Color color) {
        // BuildSphereImpl(mesh, sectors, stacks, radius, length / 2.f, SphereType::Top, color);
        // BuildCylinder(mesh, radius, 0.f, length, sectors, stacks, color);
        BuildSphereImpl(mesh, sectors, stacks, radius, -length / 2.f, SphereType::Bottom, color);
    }

    void MeshUtil::BuildCylinder(Mesh* mesh, float radius_outer, float radius_inner,
        float height, uint16_t sectors, uint16_t stacks, Color color) {

        auto& datas = mesh->vertex_data_list;
        auto& indices = mesh->vertex_index_list;

        uint16_t data_size = datas.size();

        float half_height = height / 2.f;
        bool is_radius_inner = radius_inner > 0.f;
        float stack_step = height / stacks;
        float sector_step = 2.f * PI / sectors;

        // 顶部和底部圆顶点
        // 0 : 顶部
        // 1 : 底部
        // 内 3 1
        // 外 2 0
        for (int i = 0; i < 2; i++) {
            float y = (i == 0) ? half_height : -half_height;
            float normal_y = (i == 0) ? 1.f : -1.f;
            // 不存在内部圆环添加中心点
            if (!is_radius_inner) 
                datas.push_back({{0.f, y, 0.f}, {0.f, normal_y, 0.f}, {0.5f, 0.5f}, color});
            // 顶部和底部圆顶点
            for (int j = 0; j <= sectors; j++) {
                float theta = j * sector_step;
                float sin_theta = std::sin(theta);
                float cos_theta = std::cos(theta);
                datas.push_back({{radius_outer * cos_theta, y, radius_outer * sin_theta}, {0.f, normal_y, 0.f}, 
                    {cos_theta * 0.5f + 0.5f, sin_theta * 0.5f + 0.5f}, color});

                if (is_radius_inner)
                    datas.push_back({{radius_inner * cos_theta, y, radius_inner * sin_theta}, {0.f, normal_y, 0.f}, 
                        {cos_theta * 0.5f + 0.5f, sin_theta * 0.5f + 0.5f}, color});

            }
            if (!is_radius_inner) {
                for (int j = 1; j <= sectors; j++) {
                    indices.push_back(data_size);
                    indices.push_back(data_size + j + 1);
                    indices.push_back(data_size + j);
                }
            }
            else {
                for (int j = 0; j < 2 * sectors; j++) {
                    if (j % 2 == 0) {
                        indices.push_back(data_size + j);
                        indices.push_back(data_size + j + 1);
                        indices.push_back(data_size + j + 2);                       
                    }
                    else {
                        indices.push_back(data_size + j);
                        indices.push_back(data_size + j + 2);
                        indices.push_back(data_size + j + 1);                       
                    }
                }
            }
            data_size = datas.size();
        }
        
        // 侧面
        for (int i = 0; i <= stacks; i++) {
            for (int j = 0; j <= sectors; j++) {
                float theta = j * sector_step;
                float cos_theta = std::cos(theta);
                float sin_theta = std::sin(theta);
                float y = -half_height + i * stack_step;
                datas.push_back({{radius_outer * cos_theta, y, radius_outer * sin_theta}, {cos_theta, 0.f, sin_theta}, 
                    {cos_theta * 0.5f + 0.5f, sin_theta * 0.5f + 0.5f}, color});
                if (is_radius_inner)
                    datas.push_back({{radius_inner * cos_theta, y, radius_inner * sin_theta}, {-cos_theta, 0.f, -sin_theta}, 
                        {cos_theta * 0.5f + 0.5f, sin_theta * 0.5f + 0.5f},color});
            }
        }

        for (int i = 0; i < stacks; i++) {
            for (int j = 0; j < (1 + is_radius_inner) * sectors; j++) {
                uint16_t sectors_count = (1 + is_radius_inner) * (sectors + 1);
                uint16_t idx1 = i * sectors_count + j + data_size;
                uint16_t idx2 = idx1 + sectors_count;     
                uint16_t idx3 = idx1 + 1 + is_radius_inner;
                uint16_t idx4 = idx2 + 1 + is_radius_inner;
                if (j / (1 + is_radius_inner) * sector_step > PI) std::swap(idx1, idx4);
                indices.push_back(idx3);
                indices.push_back(idx1);
                indices.push_back(idx4);

                indices.push_back(idx1);
                indices.push_back(idx2);
                indices.push_back(idx4);

            }
        }
    }

    void MeshUtil::BuildTorus(Mesh* mesh,
                              float large_radius, float small_radius, int segments_w, int segments_h) {

    }
    void MeshUtil::BuildTriangularPyramid(Mesh* mesh,
                                          float width, float height, float depth, Color color[4]) {
        float w = width / 2.f;
        float h = height / 2.f;
        float d = depth / 2.f;
        Vertex left_v1 = {{+w, -h, -d}, {}, {0, 0}, color[0]};
        Vertex left_v2 = {{0, +h, 0}, {}, {0, 0}, color[0]};
        Vertex left_v3 = {{0, -h, +d}, {}, {0, 0}, color[0]};
        Vertex right_v1 = {{-w, -h, -d}, {}, {0, 0}, color[1]};
        Vertex right_v2 = {{0, -h, +d}, {}, {0, 0}, color[1]};
        Vertex right_v3 = {{0, +h, 0}, {}, {0, 0}, color[1]};
        Vertex back_v1 = {{0, +h, 0}, {}, {0, 0}, color[2]};
        Vertex back_v2 = {{+w, -h, -d}, {}, {0, 0}, color[2]};
        Vertex back_v3 = {{-w, -h, -d}, {}, {0, 0}, color[2]};
        Vertex bottom_v1 = {{0, -h, +d}, {}, {0, 0}, color[3]};
        Vertex bottom_v2 = {{+w, -h, -d}, {}, {0, 0}, color[3]};
        Vertex bottom_v3 = {{-w, -h, -d}, {}, {0, 0}, color[3]};
        mesh->AddFace(left_v1, left_v2, left_v3);//bottom
        mesh->AddFace(right_v1, right_v2, right_v3);//back
        mesh->AddFace(back_v1, back_v2, back_v3);//left
        mesh->AddFace(bottom_v1, bottom_v2, bottom_v3);//right
    }

    void MeshUtil::FindMinMaxXYZ(Mesh* mesh, Vec3& min, Vec3& max) {
        const auto& v = mesh->vertex_data_list;
        float min_value = std::numeric_limits<float>::max();
        float max_value = std::numeric_limits<float>::min();
        min = {min_value, min_value, min_value};
        max = {max_value, max_value, max_value};
        for (const auto& vertex : v) {
            float x = vertex.position.x;
            float y = vertex.position.y;
            float z = vertex.position.z;
            min.x = std::min(min.x, x);
            min.y = std::min(min.y, y);
            min.z = std::min(min.z, z);
            max.x = std::max(max.x, x);
            max.y = std::max(max.y, y);
            max.z = std::max(max.z, z);
        }
    }
NS_RIDER_END
